#include "qelib.h"
#include "qe_mmc.h"

QELOG_DOMAIN("cmd-mmc");

static qe_const_str usages[] = {
    "mmc [options] [[--] args]",
    "mmc [options]",
    QE_NULL,
};

static char current_device[32] = {'\0'};

static qe_ret action_list_mmc_devices(qe_argparse *parser, qe_option *option)
{
    qe_mmc_list_devices();
    qe_printf("current mmc device %s\r\n", current_device);
    return qe_yield;
}

static qe_ret action_set_current_device(qe_argparse *parser, qe_option *option)
{
    qe_ret ret;
    qe_dev *dev = qe_dev_find(qe_option_string(option));
    if (!dev) {
        qe_printf("%s not found\r\n");
        return qe_yield;
    }

    ret = qe_mmc_init_device((qe_mmc *)dev, qe_true);
    if (ret != qe_ok) {
        qe_printf("%s init error:%d\r\n", qe_option_string(option), ret);
        return qe_yield;
    }

    qe_sprintf(current_device, "%s", qe_option_string(option));
    qe_printf("set current mmc device %s", current_device);

    return qe_yield;
}

static qe_ret cmd_read_block(qe_argparse *parser, qe_option *option)
{
    int n;
    char *rxbuf;
    qe_ret ret;
    qe_u32 start = 0;
    qe_u32 count = 0;
    qe_argparse subparser;

    qe_mmc *mmc = (qe_mmc *)qe_dev_find(current_device);
    if (!mmc) {
        qe_printf("no such device %s\r\n", current_device);
        return qe_yield;
    }

    qe_option options[] = {
        QE_OPT_INTEGER('s', "start", "start block", &start),
        QE_OPT_INTEGER('c', "count", "block count", &count),
        QE_OPT_HELP('h', "--help", "mmc help"),
        QE_OPT_END(),
    };

    qe_argp_init(&subparser, options, usages,
        "\nmmc read block command.", 
        "\nmmc read block command end.");

    ret = qe_argp_parse(&subparser, parser->argc, parser->argv);
    if (ret != qe_ok) {
        return qe_yield;
    }

    if (!count) {
        qe_printf("count zero\r\n");
        return qe_yield;
    }

    rxbuf = qe_malloc(count * 512);
    if (!rxbuf) {
        return qe_yield;
    }

    n = qe_mmc_bread(mmc, start, count, rxbuf);
    if (n != count) {
        qe_printf("read start:%d count:%d error:%d\r\n", start, count, n);
    } else {
        qe_printf("read start:%d count:%d success\r\n", start, count);
        qe_hexdump_info(rxbuf, count * 512);
    }

    qe_free(rxbuf);

    return qe_yield;
}

static qe_ret cmd_write_block(qe_argparse *parser, qe_option *option)
{
    int i;
    int n;
    char *txbuf;
    qe_ret ret;
    qe_u32 start = 0;
    qe_u32 count = 0;
    qe_argparse subparser;

    qe_mmc *mmc = (qe_mmc *)qe_dev_find(current_device);
    if (!mmc) {
        qe_printf("no such device %s\r\n", current_device);
        return qe_yield;
    }

    qe_option options[] = {
        QE_OPT_INTEGER('s', "start", "start block", &start),
        QE_OPT_INTEGER('c', "count", "block count", &count),
        QE_OPT_HELP('h', "--help", "mmc help"),
        QE_OPT_END(),
    };

    qe_argp_init(&subparser, options, usages,
        "\nmmc write block command.", 
        "\nmmc write block command end.");

    ret = qe_argp_parse(&subparser, parser->argc, parser->argv);
    if (ret != qe_ok) {
        return qe_yield;
    }

    txbuf = qe_malloc(count * 512);
    if (!txbuf) {
        return qe_yield;
    }

    for (i=0; i<count * 512; i++) {
        txbuf[i] = i;
    }

    n = qe_mmc_bwrite(mmc, start, count, txbuf);
    if (n != count) {
        qe_printf("write start:%d count:%d error:%d\r\n", start, count, n);
    } else {
        qe_printf("write start:%d count:%d success\r\n", start, count);
    }

    qe_free(txbuf);

    return qe_yield;
}

static qe_ret cmd_erase_block(qe_argparse *parser, qe_option *option)
{
    int n;
    qe_ret ret;
    qe_u32 start = 0;
    qe_u32 count = 0;
    qe_argparse subparser;

    qe_mmc *mmc = (qe_mmc *)qe_dev_find(current_device);
    if (!mmc) {
        qe_printf("no such device %s\r\n", current_device);
        return qe_yield;
    }

    qe_option options[] = {
        QE_OPT_INTEGER('s', "start", "start block", &start),
        QE_OPT_INTEGER('c', "count", "block count", &count),
        QE_OPT_HELP('h', "help", "mmc help"),
        QE_OPT_END(),
    };

    qe_argp_init(&subparser, options, usages,
        "\nmmc erase block command.",
        "\nmmc erase block command end.");

    ret = qe_argp_parse(&subparser, parser->argc, parser->argv);
    if (ret != qe_ok) {
        return qe_yield;
    }

    n = qe_mmc_berase(mmc, start, count);
    if (n != count) {
        qe_printf("erase start:%d count:%d error:%d\r\n", start, count, n);
    } else {
        qe_printf("erase start:%d count:%d success\r\n", start, count);
    }

    return qe_yield;
}

static void cmd_mmc(int argc, const void *argvs[])
{
    qe_str name = QE_NULL;
    qe_argparse parser;

    qe_option options[] = {
        QE_OPT_ACTION('l', "list", "list mmc devices",
            QE_NULL, action_list_mmc_devices),
        QE_ACT_STRING('d', "device", "set current mmc device",
            &name, QE_NULL, action_set_current_device),
        QE_OPT_SUBCMD("rb", "read block",
            QE_NULL, cmd_read_block),
        QE_OPT_SUBCMD("wb", "write block",
            QE_NULL, cmd_write_block),
        QE_OPT_SUBCMD("eb", "erase block",
            QE_NULL, cmd_erase_block),
        QE_OPT_HELP('h', "--help", "mmc help"),
        QE_OPT_END(),
    };

    qe_argp_init(&parser, options, usages,
    	"\nmmc command.",
		"\nmmc command.");

    qe_argp_parse(&parser, argc, (qe_const_str *)argvs);
}
QE_SHELL_EXPORT_CMD_EXEC(mmc, cmd_mmc, mmc commands);
